package ua.org.extjsbuilder.parser.runtime.builder;

import java.io.EOFException;
import java.util.LinkedList;
import java.util.List;

import ua.org.extjsbuilder.parser.IInstruction;
import ua.org.extjsbuilder.parser.runtime.NumberLiteralToken;
import ua.org.extjsbuilder.parser.runtime.RegexpLiteralToken;
import ua.org.extjsbuilder.parser.runtime.StringLiteralToken;
import ua.org.extjsbuilder.parser.runtime.UnparsedToken;
import ua.org.extjsbuilder.parser.runtime.data.JSNumber;
import ua.org.extjsbuilder.parser.runtime.data.JSRegExp;
import ua.org.extjsbuilder.parser.runtime.data.JSString;
import ua.org.extjsbuilder.parser.runtime.instructions.AccessProperty;
import ua.org.extjsbuilder.parser.runtime.instructions.Empty;
import ua.org.extjsbuilder.parser.runtime.instructions.FunctionCall;
import ua.org.extjsbuilder.parser.runtime.instructions.GetEntity;
import ua.org.extjsbuilder.parser.scanner.CharPattern;

public class ExpressionsInterpreter extends OperatorsInterpreter {
	private static final CharPattern STOP_PATTERN = 
		CharPattern.compile("?:;)}");
	
	private CharPattern stopPattern;
	
	ExpressionsInterpreter(UnparsedToken start,int pos){
		this(start,pos,STOP_PATTERN);
	}
	
	protected void checkForLeftEndState() throws EOFException {
		if((pos+1)<data.length&&
			!stopPattern.match(data[pos])){
			super.checkForLeftEndState();
		}
		else {
			analyzeKeyword();
			skipWhiteSpaces();
			if(stopPattern.match(data[pos])){
				state = LEFT_START;
			}
		}
	}

	@Override
	protected boolean isInstructionEnd() throws EOFException {
		if(data.length>pos&&stopPattern.match(data[pos])){
			return true;
		}
		ExpressionsInterpreter whiteSpacer = new ExpressionsInterpreter(token,pos+1);
		whiteSpacer.skipWhiteSpaces();
		if(whiteSpacer.match(stopPattern)){
			return true;
		}
		if(token.getLine()<whiteSpacer.token.getLine()){
			if(whiteSpacer.isOperatorChain()){
				return false;
			}
			state = LEFT_START;
			return true;
		}
		return super.isInstructionEnd();
	}

	ExpressionsInterpreter(UnparsedToken start,int pos,CharPattern stopPattern) {
		super(start);
		this.pos = pos;
		this.stopPattern = stopPattern;
	}
	
	public IInstruction buildExpression() {
		previous = null;
		lastComplete = null;
		return buildExpressionInternal();
	}

	private IInstruction buildExpressionInternal() {
		try{
			boolean check = true;
			if(data.length<=pos||isWhiteSpacesLine()){
				nextToken(false);
				if(data==null){
					defineNextLiteral();
				}
				check = false;
			}
			while(check||
				!stopPattern.match(data[pos])&&
				((lastComplete==null&&previous==null)
						||state!=LEFT_START)){
				next();
				check = false;
			}
		}
		catch(EOFException ex){
			if(state!=LEFT_START){
				err("Unexpectd end of file");
			}
		}
		if(lastComplete==null){
			return previous;
		}
		return lastComplete;
	}

	private void defineNextLiteral() throws EOFException {
		if(literal instanceof NumberLiteralToken){
			previous = new GetEntity(new JSNumber(
					(NumberLiteralToken)literal));
		}
		else if(literal instanceof RegexpLiteralToken){
			previous = new GetEntity(new JSRegExp(
					(RegexpLiteralToken)literal));
		}
		else if(literal instanceof StringLiteralToken){
			previous = new GetEntity(new JSString(
					(StringLiteralToken)literal));
		}
		nextToken(true);
		if(!isInstructionEnd()){
			ExpressionsInterpreter delegate = new ExpressionsInterpreter(token, pos);
			delegate.setState(this, true);
			delegate.pos--;
			delegate.state = EXPECT_OPERATOR;
			lastComplete = delegate.buildExpressionInternal();
			setState(delegate);
		}
	}

	@Override
	protected void parseAccessProperty() throws EOFException {
		pos++;
		skipWhiteSpaces();
		StringBuilder prop = new StringBuilder();
		while(pos<data.length&&JS_INDEFIER
				.match(data[pos])){
			prop.append(data[pos]);
			pos++;
		}
		previous = new AccessProperty(previous,prop.toString());
		state = LEFT_END;
	}

	@Override
	protected void parseFunctionCall() throws EOFException {
		List<IInstruction> arguments = new LinkedList<IInstruction>();
		skipWhiteSpaces();
		while(data[pos]!=')'){
			arguments.add(buildExpression());
			skipWhiteSpaces();
			if(data[pos]==')')continue;
			if(data[pos]!=','){
				err("Coma expected");
			}
			pos++;
			skipWhiteSpaces();
		}
		previous = new FunctionCall(previous,arguments);
		pos++;
		state = LEFT_END;
	}
	
	@Override
	protected void analyzeKeyword() throws EOFException{
		super.analyzeKeyword();
		IInstruction instr = lastComplete==null?previous:lastComplete;
		if(instr==null)return;
		String name = instr.getName();
		if(name!=null&&name.length()==1&&
			stopPattern.match(instr
					.getName().charAt(0))){
			if(lastComplete==null)previous = new Empty();
			else lastComplete = new Empty();
		}
	}
}
